﻿Imports System.Globalization
Imports System.IO
Imports System.Runtime.InteropServices
Imports System.Text.Json
Imports System.Windows
Imports System.Windows.Forms.VisualStyles.VisualStyleElement.Window

Public Class FormCalendar
    ' 用 API 发送拖动消息
    <DllImport("user32.dll")>
    Private Shared Function ReleaseCapture() As Boolean
    End Function

    <DllImport("user32.dll")>
    Private Shared Function SendMessage(hWnd As IntPtr, Msg As Integer, wParam As Integer, lParam As Integer) As Integer
    End Function

    Private Const WM_NCLBUTTONDOWN As Integer = &HA1
    Private Const HTCAPTION As Integer = 2

    '===== 对外：主窗体可以订阅这个回调，在新增/变更任务后刷新 TaskInfo =====
    Public Property OnTasksChanged As Action

    Public currentMonth As Date = New Date(Today.Year, Today.Month, Today.Day)
    Private cnCal As New ChineseLunisolarCalendar()

    ' 弹出任务面板及其内容
    Private taskPopup As Panel
    Private taskListBox As ListBox
    Private taskHideTimer As Timer

    Private ReadOnly dataDir As String = TaskInfo.todoDataPath

    Private ReadOnly tasksFile As String

    Public Sub New()
        InitializeComponent()
        tasksFile = Path.Combine(dataDir, "tasks.json")
    End Sub

    Private Sub FormCalendar_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        Me.Hide()
        SetControlRound(Me, 10)


        InitTaskPopup()
        ' 窗口不在任务栏显示
        Me.ShowInTaskbar = False
        DesktopEmbed.EmbedToDesktop(Me)

        InitTable()
        LoadTerms()

        RenderMonth()
        Me.Show()
        AddHandler TaskStore.TasksChanged, Sub()
                                               SafeUI(Sub() RenderMonth())
                                           End Sub
    End Sub

    Private Sub SafeUI(action As Action)
        If Me.IsHandleCreated AndAlso Me.InvokeRequired Then
            Me.BeginInvoke(action)
        Else
            action()
        End If
    End Sub

    '================= 顶部导航 =================
    Private Sub btnPrev_Click(sender As Object, e As EventArgs) Handles btnPrev.Click
        currentMonth = currentMonth.AddMonths(-1)
        RenderMonth()
    End Sub

    Private Sub btnToday_Click(sender As Object, e As EventArgs) Handles btnToday.Click
        currentMonth = New Date(Today.Year, Today.Month, Today.Day)
        RenderMonth()
    End Sub

    Private Sub btnNext_Click(sender As Object, e As EventArgs) Handles btnNext.Click
        currentMonth = currentMonth.AddMonths(1)
        RenderMonth()
    End Sub

    '================= 表格初始化 =================
    Private Sub InitTable()
        tblCalendar.Visible = False
        tblCalendar.RowStyles.Clear()
        tblCalendar.ColumnStyles.Clear()

        tblCalendar.ColumnCount = 7
        tblCalendar.RowCount = 7 ' 1行星期 + 6行日期

        For i = 0 To 6
            tblCalendar.ColumnStyles.Add(New ColumnStyle(SizeType.Percent, 100.0F / 7))
        Next
        For r = 0 To 6
            Dim h As Single = If(r = 0, 9.0F, 15.1667F) ' 顶部星期行略矮
            tblCalendar.RowStyles.Add(New RowStyle(SizeType.Percent, h))
        Next

        tblCalendar.Controls.Clear()

        ' 星期抬头
        Dim wks = {"一", "二", "三", "四", "五", "六", "日"}
        For c = 0 To 6
            Dim lbl As New Label With {
                .Dock = DockStyle.Fill,
                .TextAlign = ContentAlignment.MiddleCenter,
                .Font = New Font("Microsoft YaHei", 9.0F, FontStyle.Bold),
                .ForeColor = Color.DimGray,
                .Text = "周" & wks(c)
            }
            tblCalendar.Controls.Add(lbl, c, 0)
        Next
        tblCalendar.Visible = True
    End Sub
    '===========================================

    Public Sub RenderMonth()

        If taskPopup IsNot Nothing Then taskPopup.Visible = False

        '为加快表格刷新速度，暂令tblCalendar面板不可见。处理完后再可见。
        tblCalendar.Visible = False

        ' 清空星期行以下的所有单元格
        For i = tblCalendar.Controls.Count - 1 To 7 Step -1
            tblCalendar.Controls.RemoveAt(i)
        Next

        lblMonth.Text = currentMonth.ToString("yyyy年MM月dd日")

        ' 渲染本月日期（优化，只渲染当日）
        Dim firstDay As Date = New Date(currentMonth.Year, currentMonth.Month, 1)
        ' 本地习惯：周一为第一列
        Dim dayOfWeek As Integer = (CInt(firstDay.DayOfWeek) + 6) Mod 7 ' 0=周一, 6=周日
        Dim startDate As Date = firstDay.AddDays(-dayOfWeek)

        ' 获取当前日期，用来设置底色
        Dim today As Date = Date.Today
        Dim lastJieQi As String = String.Empty

        For r = 1 To 6
            For c = 0 To 6
                Try
                    Dim d As Date = startDate.AddDays((r - 1) * 7 + c)

                    Dim cell As New DayCell()
                    cell.Visible = False
                    cell.Dock = DockStyle.Fill
                    cell.DateValue = d

                    Dim inMonth = (d.Month = currentMonth.Month)
                    cell.IsCurrentMonth = inMonth

                    ' 当前日期的格子使用不同底色
                    If d.Date = today Then
                        cell.BackColor = Color.LightYellow ' 当前日期格子底色为浅黄色
                    Else
                        cell.BackColor = Color.Transparent ' 其他日期保持透明
                    End If

                    ' 是否有任务
                    Dim hasTasks = TaskStore.GetTasksOf(d.Date).Any()
                    cell.HasTasks = hasTasks

                    ' 添加绿色圆点标记
                    If hasTasks Then
                        Dim dot As New Label With {
                    .Width = 8,
                    .Height = 8,
                    .BackColor = Color.Green,
                    .BorderStyle = BorderStyle.None,
                    .Location = New Point((cell.Width - 8) \ 2, (cell.Height - 8) \ 2)
                }
                        cell.Controls.Add(dot)
                    End If
                    ' 文案：公历日
                    cell.MainText = d.Day.ToString()

                    ' 农历 & 自定义节气/备注
                    Dim extra As String = GetCustomOrLunarText(d)
                    cell.SubText = extra

                    ' 增加节气信息显示
                    Dim lunar1 = New Lunar.Lunar(d)
                    Dim JieQi As String = lunar1.Hou.Substring(0, 2)
                    'cell.SubText &= vbCrLf & JieQi ' 在农历下面添加节气信息

                    ' 仅在节气发生变化时更新显示
                    If JieQi <> lastJieQi Then
                        ' 更新显示节气信息
                        cell.SubText &= "  " & JieQi
                        lastJieQi = JieQi ' 更新为当前节气
                    End If

                    ' 可选：写入其他汉字信息
                    cell.OtherText = "自定义" ' 预留自定义显示文字

                    ' 点击：新增任务
                    ' 单元点击：保持 TaskStore.Add
                    AddHandler cell.DayClicked, Sub()
                                                    Dim frm As New FormEditTask With {.TaskDate = d.Date, .DueAt = d.Date.AddHours(9)}
                                                    If frm.ShowDialog() = DialogResult.OK Then
                                                        TaskStore.Add(New TaskItem With {
                                                        .TaskDate = frm.TaskDate.Date,
                                                        .Description = frm.TaskDescription,
                                                        .DueAt = frm.DueAt,
                                                        .IsCompleted = False,
                                                        .HasNotified = False
                                                    })
                                                        ' 不再手动 RenderMonth()，事件会驱动
                                                    End If
                                                End Sub

                    ' 当鼠标进入某个日期时显示任务列表
                    AddHandler cell.MouseEnter, Sub()
                                                    ShowTaskPopup(d.Date, cell)
                                                End Sub

                    ' 鼠标离开日期时，启动一个短延迟，若未移到弹层上则隐藏
                    AddHandler cell.MouseLeave, Sub()
                                                    taskHideTimer.Stop()
                                                    taskHideTimer.Start()
                                                End Sub

                    tblCalendar.Controls.Add(cell, c, r)
                    cell.Visible = True
                Catch ex As Exception
                    Debug.Print(ex.Message)
                End Try

            Next
        Next
        tblCalendar.Visible = True
    End Sub

    Private Function FindCellForDate(d As Date) As DayCell
        ' 遍历 tblCalendar 的所有控件（假设每个控件都是 DayCell）
        For Each ctrl As Control In tblCalendar.Controls
            ' 确保控件是 DayCell 类型
            If TypeOf ctrl Is DayCell Then
                Dim cell As DayCell = CType(ctrl, DayCell)

                ' 检查该 DayCell 的 DateValue 是否匹配给定的日期
                If cell.DateValue.Date = d.Date Then
                    Return cell
                End If
            End If
        Next

        ' 如果未找到匹配的 DayCell，返回 Nothing
        Return Nothing
    End Function

    '================= 农历/节气文本 =================
    Private Function GetCustomOrLunarText(d As Date) As String

        Try
            ' 获取公历的年份、月份、是否有闰月
            Dim cnCal As New ChineseLunisolarCalendar()
            Dim y = cnCal.GetYear(d)
            Dim m = cnCal.GetMonth(d)
            Dim leap = cnCal.GetLeapMonth(y) ' 闰月序号（无则0）

            ' 判断是否是闰月
            Dim isLeap As Boolean = (leap > 0 AndAlso m = leap)

            ' 如果有闰月并且当前月份大于闰月的序号，调整月份
            If leap > 0 Then
                If m > leap Then
                    m -= 1
                ElseIf m = leap Then
                    m -= 1
                End If
            End If

            ' 获取农历月份和日期的名称
            Dim lunarMonthName = GetLunarMonthName(m, isLeap)
            Dim lunarDayName = GetLunarDayName(cnCal.GetDayOfMonth(d))

            ' 返回农历日期
            Return $"{lunarMonthName}{lunarDayName}"
        Catch ex As Exception
            Return ""
        End Try

    End Function

    Private Function GetLunarMonthName(m As Integer, isLeap As Boolean) As String
        Dim names = {"正", "二", "三", "四", "五", "六", "七", "八", "九", "十", "冬", "腊"}
        Dim title = If(m >= 1 AndAlso m <= 12, names(m - 1), m.ToString())
        Return If(isLeap, "闰" & title & "月", title & "月")
    End Function

    Private Function GetLunarDayName(d As Integer) As String
        Dim ten = {"初", "十", "廿", "卅"}
        Dim num = {"零", "一", "二", "三", "四", "五", "六", "七", "八", "九", "十"}
        If d <= 10 Then Return "初" & num(d)
        If d = 20 Then Return "二十"
        If d = 30 Then Return "三十"
        Dim t = ten((d \ 10))
        Dim u = num(d Mod 10)
        Return t & u
    End Function

    '================= terms.json 读取/初始化 =================
    Private Sub LoadTerms()
        ' 加载任务数据
        TaskStore.LoadTasksFromDisk()

        ' 如果任务为空时，创建一个示例任务
        If TaskStore.Tasks.Count = 0 Then
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today, .Description = "上午开会", .DueAt = Date.Today.AddHours(9)})
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today, .Description = "下午写报告", .DueAt = Date.Today.AddHours(14)})
            TaskStore.Add(New TaskItem With {.TaskDate = Date.Today.AddDays(1), .Description = "交付项目文档", .DueAt = Date.Today.AddDays(1).AddHours(10)})
        End If

        TaskInfo.RenderTasks()

        ' 订阅任务变更事件，当任务发生变更时，更新界面
        AddHandler TaskStore.TasksChanged, Sub()
                                               ' 确保 UI 更新在主线程
                                               SafeUI(Sub() TaskInfo.RenderTasks())
                                           End Sub
    End Sub


    Private Sub FormCalendar_MouseDown(sender As Object, e As MouseEventArgs) Handles Me.MouseDown
        If e.Button = MouseButtons.Left Then
            ReleaseCapture()
            SendMessage(Me.Handle, WM_NCLBUTTONDOWN, HTCAPTION, 0)
        End If
    End Sub

    Private Sub InitTaskPopup()
        ' Panel 作为弹层容器
        taskPopup = New Panel With {
            .Visible = False,
            .BackColor = Color.White,
            .Padding = New Padding(8),
            .Width = 320,
            .Height = 220,
            .BorderStyle = BorderStyle.None
        }

        ' ListBox 展示任务（可滚动）
        taskListBox = New ListBox With {
            .Dock = DockStyle.Fill,
            .Font = New Font("Microsoft YaHei", 10.0F, FontStyle.Regular),
            .BorderStyle = BorderStyle.None,
            .IntegralHeight = False, ' 允许自定义高度
            .DrawMode = DrawMode.OwnerDrawVariable ' 设置为可变高度模式
        }
        taskPopup.Controls.Add(taskListBox)

        ' 添加到窗体并置顶
        Me.Controls.Add(taskPopup)
        taskPopup.BringToFront()

        ' 注册自定义绘制事件
        AddHandler taskListBox.DrawItem, AddressOf taskListBox_DrawItem
        AddHandler taskListBox.MeasureItem, AddressOf taskListBox_MeasureItem ' 注册MeasureItem事件

        ' 延迟隐藏用的定时器（避免在从日期移到面板时闪烁）
        taskHideTimer = New Timer With {.Interval = 200}
        AddHandler taskHideTimer.Tick, AddressOf TaskHideTimer_Tick

        ' 鼠标移动到弹层上时不隐藏，离开时再启动隐藏
        AddHandler taskPopup.MouseEnter, Sub() taskHideTimer.Stop()
        AddHandler taskPopup.MouseLeave, Sub() taskHideTimer.Start()
    End Sub

    ' MeasureItem 事件处理函数，用于动态设置每个项的高度
    Private Sub taskListBox_MeasureItem(sender As Object, e As MeasureItemEventArgs)
        ' 获取 ListBox 控件
        Dim listBox1 As ListBox = CType(sender, ListBox)

        ' 获取当前项的文本
        Dim itemText As String = listBox1.Items(e.Index).ToString()

        ' 创建格式化文本的方式，设置文本自动换行
        Dim format As New StringFormat()
        format.Trimming = StringTrimming.Word
        format.FormatFlags = StringFormatFlags.LineLimit

        ' 计算文本的大小，获取每个项的高度
        Dim size As SizeF = e.Graphics.MeasureString(itemText, listBox1.Font, listBox1.Width, format)

        ' 设置该项的高度
        e.ItemHeight = CInt(size.Height) + 4 ' 可根据需要适当调整加上额外的间隔
    End Sub


    ' 自定义绘制 ListBox 项
    Private Sub taskListBox_DrawItem(sender As Object, e As DrawItemEventArgs)
        ' 获取 ListBox 控件
        Dim listBox1 As ListBox = CType(sender, ListBox)

        ' 获取当前项的文本
        Dim itemText As String = listBox1.Items(e.Index).ToString()

        ' 设置绘制背景色
        e.DrawBackground()

        ' 设置文本颜色
        Dim textColor As Brush = If((e.State And DrawItemState.Selected) = DrawItemState.Selected, Brushes.Red, Brushes.Black)

        ' 创建格式化文本的方式，设置文本自动换行
        Dim format As New StringFormat()
        format.Trimming = StringTrimming.Word
        format.FormatFlags = StringFormatFlags.LineLimit

        ' 绘制文本
        e.Graphics.DrawString(itemText, e.Font, textColor, e.Bounds, format)

        ' 绘制边框
        e.DrawFocusRectangle()
    End Sub

    Private Sub ShowTaskPopup(d As Date, anchor As Control)
        ' 1) 准备数据：带时间 + 描述
        Dim tasks = TaskStore.GetTasksOf(d.Date).
                 Where(Function(t) Not t.IsCompleted). ' 排除已完成的任务
                 OrderBy(Function(t) t.DueAt).
                 ToList()

        '2）添加任务项
        taskListBox.Items.Clear()
        For Each t In tasks
            taskListBox.Items.Add($"{t.DueAt:HH:mm}  {t.Description}")
        Next

        ' 如果没有任务，直接隐藏弹出框
        If taskListBox.Items.Count = 0 Then
            taskPopup.Visible = False
            Return
        End If

        ' 3) 动态高度（最多 10 条左右）
        Dim itemH = taskListBox.ItemHeight
        Dim wantedH = 8 + Math.Min(10, taskListBox.Items.Count) * itemH + 8
        taskPopup.Height = Math.Max(120, Math.Min(320, wantedH))
        taskPopup.Width = 340 ' 适当加宽一点

        ' 确保弹窗大小适应任务内容
        taskPopup.Refresh() ' 强制刷新弹窗

        ' 4) 计算位置：把 anchor(=DayCell) 的坐标转换到窗体坐标
        Dim anchorScreen As Point = anchor.PointToScreen(Point.Empty)
        Dim anchorOnForm As Point = Me.PointToClient(anchorScreen)

        Dim x As Integer = anchorOnForm.X + anchor.Width + 8   ' 优先放在右侧
        Dim y As Integer = anchorOnForm.Y                      ' 顶对齐

        ' 5) 边界避让：若放右侧超出，就放到左侧；底部超出则上移
        If x + taskPopup.Width > Me.ClientSize.Width Then
            x = anchorOnForm.X - taskPopup.Width - 8
        End If
        If y + taskPopup.Height > Me.ClientSize.Height Then
            y = Me.ClientSize.Height - taskPopup.Height - 8
        End If
        If y < 0 Then y = 0
        If x < 0 Then x = 0

        taskPopup.Location = New Point(x, y)

        ' 6) 显示并置顶
        taskPopup.BackColor = Color.AliceBlue
        SetControlRound(taskPopup, 7)

        taskPopup.Visible = True
        taskPopup.BringToFront()
        taskHideTimer.Stop()
    End Sub

    Private Sub TaskHideTimer_Tick(sender As Object, e As EventArgs)
        ' 鼠标不在弹层内且不在任何 DayCell 上，则隐藏
        Dim pt As Point = Me.PointToClient(Cursor.Position)
        Dim overPopup As Boolean = taskPopup.Bounds.Contains(pt)

        Dim overAnyDayCell As Boolean = False
        For Each ctrl As Control In tblCalendar.Controls
            If TypeOf ctrl Is DayCell Then
                Dim r As Rectangle = RectangleToClient(ctrl.RectangleToScreen(ctrl.ClientRectangle))
                If r.Contains(pt) Then
                    overAnyDayCell = True
                    Exit For
                End If
            End If
        Next

        If Not overPopup AndAlso Not overAnyDayCell Then
            taskPopup.Visible = False
            taskHideTimer.Stop()
        End If
    End Sub

    Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
        Me.Hide()
    End Sub
End Class

